#ifndef __CGE_VECTOR3_H__
#define __CGE_VECTOR3_H__

#include "cgeCommons.h"

namespace cge
{
	namespace std
	{
		template<class TVal>
		class Vector3
		{
		public:
			Vector3()
			{
				Set(0, 0, 0);
			}
			
			Vector3(const Vector3& sz)
			{
				Set(sz.X, sz.Y, sz.Z);
			}

			Vector3(TVal x, TVal y, TVal z)
			{
				Set(x, y, z);
			}

			f32 Length() const
			{
				return Math::Sqrt(X * X + Y * Y + Z * Z);
			}

			void Set(TVal x, TVal y, TVal z)
			{
				X = x;	Y = y;	Z = z;
			}

			f32 Dot(const Vector3& v3) const
			{
				return (X * v3.X + Y * v3.Y + Z * v3.Z);
			}

			Vector3 Cross(const Vector3& v3) const
			{
				return Vector3((Y * v3.Z) - (Z * v3.Y), 
							   (Z * v3.X) - (X * v3.Z),
							   (X * v3.Y) - (Y * v3.X));
			}

			f32 AngleBetween(const Vector3& v3) const
			{
				return Math::Acos(Dot(v3));
			}

			void Normalize()
			{
				f32 len = Length();
				X /= len;
				Y /= len;
				Z /= len;
			}

			template<class TArg>
			Vector3 operator * (TArg scalar) const
			{
				return Vector3(X * scalar, Y * scalar, Z * scalar);
			}

			template<class TArg>
			Vector3 operator / (TArg scalar) const
			{
				return Vector3(X / scalar, Y / scalar, Z / scalar);
			}

			Vector3 operator - (void) const
			{
				return Vector3(-X, -Y, -Z);
			}

			friend Vector3 operator + (const Vector3& lsz, const Vector3& rsz)
			{
				return Vector3(lsz.X + rsz.X , lsz.Y + rsz.Y, lsz.Z + rsz.Z);
			}

			friend Vector3 operator - (const Vector3& lsz, const Vector3& rsz)
			{
				return Vector3(lsz.X - rsz.X , lsz.Y - rsz.Y, lsz.Z - rsz.Z);
			}

			friend b8 operator == (const Vector3& lsz, const Vector3& rsz)
			{
				return (lsz.X == rsz.X) && (lsz.Y == rsz.Y) && (lsz.Z == rsz.Z);
			}
			
			friend b8 operator != (const Vector3& lsz, const Vector3& rsz)
			{
				return (lsz.X != rsz.X) || (lsz.Y != rsz.Y) || (lsz.Z != rsz.Z);
			}

			template<class TArg>
			Vector3& operator *= (TArg scalar)
			{
				X *= scalar;	Y *= scalar;	Z *= scalar; 
				return *this;
			}

			template<class TArg>
			Vector3& operator /= (TArg scalar)
			{
				X /= scalar;	Y /= scalar;	Z /= scalar;
				return *this;
			}

			Vector3& operator += (const Vector3& sz)
			{
				X += sz.X;		Y += sz.Y;	Z += sz.Z;	
				return *this;
			}

			Vector3& operator -= (const Vector3& sz)
			{
				X -= sz.X;		Y -= sz.Y;	Z -= sz.Z;
				return *this;
			}

			Vector3& operator = (const Vector3& sz)
			{
				Set(sz.X, sz.Y), sz.Z;
				return *this;
			}

		public:
			union
			{
				struct
				{
					TVal X, Y, Z;
				};
				TVal Coords[3];
			};
		};

		typedef Vector3<s32> Vector3s;

		typedef Vector3<u32> Vector3u;

		typedef Vector3<f32> Vector3f;

		typedef Vector3<f64> Vector3d;
	}
}

#endif//__CGE_VECTOR3_H__